//
// Created by fred on 11/12/16.
//

#include <iostream>
#include <sstream>
#include <algorithm>
#include <iomanip>
#include <frnetlib/Http.h>

#include "frnetlib/Http.h"

namespace fr
{
    Http::Http()
    : request_type(Http::RequestType::Unknown),
      uri("/"),
      status(Http::RequestStatus::Ok),
      version(Http::RequestVersion::V1_1)
    {

    }

    Http::RequestType Http::get_type() const
    {
        return request_type;
    }

    std::vector<std::string> Http::split_string(const std::string &str, char token, bool strip_spacing)
    {
        std::vector<std::string> ret;
        std::string buffer;

        for(char a : str)
        {
            if(a == token)
            {
                if(!buffer.empty())
                {
                    ret.emplace_back(std::move(buffer));
                }
                buffer.clear();
            }
            else if(a != ' ' || !strip_spacing)
            {
                buffer += a;
            }
        }
        if(!buffer.empty())
        {
            ret.emplace_back(std::move(buffer));
        }

        return ret;
    }

    void Http::set_body(const std::string &body_)
    {
        body = body_;
    }

    std::string &Http::get(std::string key)
    {
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        return get_data[key];
    }

    std::string &Http::post(std::string key)
    {
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        return post_data[key];
    }

    bool Http::get_exists(std::string key) const
    {
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        return get_data.find(key) != get_data.end();
    }

    bool Http::post_exists(std::string key) const
    {
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        return post_data.find(key) != post_data.end();
    }

    std::string &Http::header(std::string key)
    {
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        return header_data[key];
    }

    bool Http::header_exists(std::string key) const
    {
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        return header_data.find(key) != header_data.end();
    }


    const std::string &Http::get_uri() const
    {
        return uri;
    }

    void Http::set_status(RequestStatus status_)
    {
        status = status_;
    }

    Http::RequestStatus Http::get_status() const
    {
        return status;
    }

    void Http::set_uri(const std::string &str)
    {
        //Don't do anything if there's no URI provided
        if(str.empty())
            return;

        //Store it, ensuring that it begins with a forwards slash
        if(str.front() == '/')
            uri = str;
        else
            uri = '/' + str;
    }

    void Http::set_type(Http::RequestType type)
    {
        request_type = type;
    }

    const std::string &Http::get_body() const
    {
        return body;
    }

    std::string Http::url_encode(const std::string &str)
    {
        static const char hex_lookup[]= "0123456789ABCDEF";
        std::string out;
        for(char c : str)
        {
            if ((c >= '0' && c <= '9') ||
                (c >= 'a' && c <= 'z') ||
                (c >= 'A' && c <= 'Z') ||
                c == '-' || c == '_' || c == '.' || c == '!' || c == '~' ||
                c == '*' || c == '\'' || c == '(' || c == ')')
            {
                out += c;
            }
            else if (c == ' ')
            {
                out += '+';
            }
            else
            {
                out.push_back('%');
                out.push_back(hex_lookup[(c&0xF0)>>4]);
                out.push_back(hex_lookup[(c&0x0F)]);
            }
        }

        return out;
    }

    std::string Http::url_decode(const std::string &str)
    {
        std::string result;
        for(size_t a = 0; a < str.size(); a++)
        {
            if(str[a] == '%' && a < str.size() - 1)
            {
                int ch1 = str[++a] - 48;
                int ch2 = str[++a] - 48;
                if(ch1 > 9) ch1 -= 7;
                if(ch2 > 9) ch2 -= 7;
                uint8_t ret = 0;
                ret |= ch1 << 4;
                ret |= ch2;
                result.push_back(ret);
            }
            else if(str[a] == '+')
            {
                result += " ";
            }
            else
            {
                result += str[a];
            }
        }
        return result;
    }

    std::vector<std::pair<std::string, std::string>> Http::parse_argument_list(const std::string &str)
    {
        std::vector<std::pair<std::string, std::string>> list;
        if(str.empty())
            return list;

        size_t read_index = 0;
        if(str.front() == '?')
            read_index++;

        while(true)
        {
            unsigned long equal_pos;
            equal_pos = str.find('=', read_index);
            if(equal_pos != std::string::npos)
            {
                unsigned long and_pos;
                and_pos = str.find('&', read_index);
                if(and_pos >= str.size())
                {
                    size_t str_size = str.size();
                    while(str[str_size - 1] == '\n' || str[str_size - 1] == '\r')
                        --str_size;
                    list.emplace_back(str.substr(read_index, equal_pos - read_index), str.substr(equal_pos + 1, str_size - equal_pos - 1));
                    break;
                }

                list.emplace_back(str.substr(read_index, equal_pos - read_index), str.substr(equal_pos + 1, and_pos - equal_pos - 1));
                    read_index = and_pos + 1;

            }
            else
            {
                break;
            }
        }

        return list;
    }

    void Http::parse_header_line(const std::string &str)
    {
        size_t colon_pos = str.find(':');
        if(colon_pos == std::string::npos)
            return;

        unsigned long data_begin;
        data_begin = str.find_first_not_of(' ', colon_pos + 1);
        if(data_begin == std::string::npos)
            return;

        size_t data_len = 0;
        for(size_t a = data_begin; a < str.size(); a++)
        {
            if(str[a] >= 32 && str[a] <= 126)
                data_len++;
            else
                break;
        }
        std::string header_name = str.substr(0, colon_pos);
        std::string header_value = str.substr(data_begin, data_len);
        std::transform(header_name.begin(), header_name.end(), header_name.begin(), ::tolower);

        //Check if it contains transfer encoding, we store this in a separate set
        if(header_name == "transfer-encoding")
        {
            auto encodings = split_string(header_value, ',', true);
            for(const auto &enc : encodings)
            {
                transfer_encodings.emplace(string_to_transfer_encoding(enc));
            }
        }

        //Store header for good
        header_data.emplace(std::move(header_name), std::move(header_value));
    }

    const std::string &Http::get_mimetype(const std::string &filename)
    {
        static std::unordered_map<std::string, std::string> known_mimetypes = {
                {".x3d", "application/vnd.hzn-3d-crossword"},
                {".3gp", "video/3gpp"},
                {".3g2", "video/3gpp2"},
                {".mseq", "application/vnd.mseq"},
                {".pwn", "application/vnd.3m.post-it-notes"},
                {".plb", "application/vnd.3gpp.pic-bw-large"},
                {".psb", "application/vnd.3gpp.pic-bw-small"},
                {".pvb", "application/vnd.3gpp.pic-bw-var"},
                {".tcap", "application/vnd.3gpp2.tcap"},
                {".7z", "application/x-7z-compressed"},
                {".abw", "application/x-abiword"},
                {".ace", "application/x-ace-compressed"},
                {".acc", "application/vnd.americandynamics.acc"},
                {".acu", "application/vnd.acucobol"},
                {".atc", "application/vnd.acucorp"},
                {".adp", "audio/adpcm"},
                {".aab", "application/x-authorware-bin"},
                {".aam", "application/x-authorware-map"},
                {".aas", "application/x-authorware-seg"},
                {".air", "application/vnd.adobe.air-application-installer-package+zip"},
                {".swf", "application/x-shockwave-flash"},
                {".fxp", "application/vnd.adobe.fxp"},
                {".pdf", "application/pdf"},
                {".ppd", "application/vnd.cups-ppd"},
                {".dir", "application/x-director"},
                {".xdp", "application/vnd.adobe.xdp+xml"},
                {".xfdf", "application/vnd.adobe.xfdf"},
                {".aac", "audio/x-aac"},
                {".ahead", "application/vnd.ahead.space"},
                {".azf", "application/vnd.airzip.filesecure.azf"},
                {".azs", "application/vnd.airzip.filesecure.azs"},
                {".azw", "application/vnd.amazon.ebook"},
                {".ami", "application/vnd.amiga.ami"},
                {"N/A", "application/andrew-inset"},
                {".apk", "application/vnd.android.package-archive"},
                {".cii", "application/vnd.anser-web-certificate-issue-initiation"},
                {".fti", "application/vnd.anser-web-funds-transfer-initiation"},
                {".atx", "application/vnd.antix.game-component"},
                {".mpkg", "application/vnd.apple.installer+xml"},
                {".aw", "application/applixware"},
                {".les", "application/vnd.hhe.lesson-player"},
                {".swi", "application/vnd.aristanetworks.swi"},
                {".s", "text/x-asm"},
                {".atomcat", "application/atomcat+xml"},
                {".atomsvc", "application/atomsvc+xml"},
                {".atom, .xml", "application/atom+xml"},
                {".ac", "application/pkix-attr-cert"},
                {".aif", "audio/x-aiff"},
                {".avi", "video/x-msvideo"},
                {".aep", "application/vnd.audiograph"},
                {".dxf", "image/vnd.dxf"},
                {".dwf", "model/vnd.dwf"},
                {".par", "text/plain-bas"},
                {".bcpio", "application/x-bcpio"},
                {".bin", "application/octet-stream"},
                {".bmp", "image/bmp"},
                {".torrent", "application/x-bittorrent"},
                {".cod", "application/vnd.rim.cod"},
                {".mpm", "application/vnd.blueice.multipass"},
                {".bmi", "application/vnd.bmi"},
                {".sh", "application/x-sh"},
                {".btif", "image/prs.btif"},
                {".rep", "application/vnd.businessobjects"},
                {".bz", "application/x-bzip"},
                {".bz2", "application/x-bzip2"},
                {".csh", "application/x-csh"},
                {".c", "text/x-c"},
                {".cdxml", "application/vnd.chemdraw+xml"},
                {".css", "text/css"},
                {".cdx", "chemical/x-cdx"},
                {".cml", "chemical/x-cml"},
                {".csml", "chemical/x-csml"},
                {".cdbcmsg", "application/vnd.contact.cmsg"},
                {".cla", "application/vnd.claymore"},
                {".c4g", "application/vnd.clonk.c4group"},
                {".sub", "image/vnd.dvb.subtitle"},
                {".cdmia", "application/cdmi-capability"},
                {".cdmic", "application/cdmi-container"},
                {".cdmid", "application/cdmi-domain"},
                {".cdmio", "application/cdmi-object"},
                {".cdmiq", "application/cdmi-queue"},
                {".c11amc", "application/vnd.cluetrust.cartomobile-config"},
                {".c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"},
                {".ras", "image/x-cmu-raster"},
                {".dae", "model/vnd.collada+xml"},
                {".csv", "text/csv"},
                {".cpt", "application/mac-compactpro"},
                {".wmlc", "application/vnd.wap.wmlc"},
                {".cgm", "image/cgm"},
                {".ice", "x-conference/x-cooltalk"},
                {".cmx", "image/x-cmx"},
                {".xar", "application/vnd.xara"},
                {".cmc", "application/vnd.cosmocaller"},
                {".cpio", "application/x-cpio"},
                {".clkx", "application/vnd.crick.clicker"},
                {".clkk", "application/vnd.crick.clicker.keyboard"},
                {".clkp", "application/vnd.crick.clicker.palette"},
                {".clkt", "application/vnd.crick.clicker.template"},
                {".clkw", "application/vnd.crick.clicker.wordbank"},
                {".wbs", "application/vnd.criticaltools.wbs+xml"},
                {".cryptonote", "application/vnd.rig.cryptonote"},
                {".cif", "chemical/x-cif"},
                {".cmdf", "chemical/x-cmdf"},
                {".cu", "application/cu-seeme"},
                {".cww", "application/prs.cww"},
                {".curl", "text/vnd.curl"},
                {".dcurl", "text/vnd.curl.dcurl"},
                {".mcurl", "text/vnd.curl.mcurl"},
                {".scurl", "text/vnd.curl.scurl"},
                {".car", "application/vnd.curl.car"},
                {".pcurl", "application/vnd.curl.pcurl"},
                {".cmp", "application/vnd.yellowriver-custom-menu"},
                {".dssc", "application/dssc+der"},
                {".xdssc", "application/dssc+xml"},
                {".deb", "application/x-debian-package"},
                {".uva", "audio/vnd.dece.audio"},
                {".uvi", "image/vnd.dece.graphic"},
                {".uvh", "video/vnd.dece.hd"},
                {".uvm", "video/vnd.dece.mobile"},
                {".uvu", "video/vnd.uvvu.mp4"},
                {".uvp", "video/vnd.dece.pd"},
                {".uvs", "video/vnd.dece.sd"},
                {".uvv", "video/vnd.dece.video"},
                {".dvi", "application/x-dvi"},
                {".seed", "application/vnd.fdsn.seed"},
                {".dtb", "application/x-dtbook+xml"},
                {".res", "application/x-dtbresource+xml"},
                {".ait", "application/vnd.dvb.ait"},
                {".svc", "application/vnd.dvb.service"},
                {".eol", "audio/vnd.digital-winds"},
                {".djvu", "image/vnd.djvu"},
                {".dtd", "application/xml-dtd"},
                {".mlp", "application/vnd.dolby.mlp"},
                {".wad", "application/x-doom"},
                {".dpg", "application/vnd.dpgraph"},
                {".dra", "audio/vnd.dra"},
                {".dfac", "application/vnd.dreamfactory"},
                {".dts", "audio/vnd.dts"},
                {".dtshd", "audio/vnd.dts.hd"},
                {".dwg", "image/vnd.dwg"},
                {".geo", "application/vnd.dynageo"},
                {".es", "application/ecmascript"},
                {".mag", "application/vnd.ecowin.chart"},
                {".mmr", "image/vnd.fujixerox.edmics-mmr"},
                {".rlc", "image/vnd.fujixerox.edmics-rlc"},
                {".exi", "application/exi"},
                {".mgz", "application/vnd.proteus.magazine"},
                {".epub", "application/epub+zip"},
                {".eml", "message/rfc822"},
                {".nml", "application/vnd.enliven"},
                {".xpr", "application/vnd.is-xpr"},
                {".xif", "image/vnd.xiff"},
                {".xfdl", "application/vnd.xfdl"},
                {".emma", "application/emma+xml"},
                {".ez2", "application/vnd.ezpix-album"},
                {".ez3", "application/vnd.ezpix-package"},
                {".fst", "image/vnd.fst"},
                {".fvt", "video/vnd.fvt"},
                {".fbs", "image/vnd.fastbidsheet"},
                {".fe_launch", "application/vnd.denovo.fcselayout-link"},
                {".f4v", "video/x-f4v"},
                {".flv", "video/x-flv"},
                {".fpx", "image/vnd.fpx"},
                {".npx", "image/vnd.net-fpx"},
                {".flx", "text/vnd.fmi.flexstor"},
                {".fli", "video/x-fli"},
                {".ftc", "application/vnd.fluxtime.clip"},
                {".fdf", "application/vnd.fdf"},
                {".f", "text/x-fortran"},
                {".mif", "application/vnd.mif"},
                {".fm", "application/vnd.framemaker"},
                {".fh", "image/x-freehand"},
                {".fsc", "application/vnd.fsc.weblaunch"},
                {".fnc", "application/vnd.frogans.fnc"},
                {".ltf", "application/vnd.frogans.ltf"},
                {".ddd", "application/vnd.fujixerox.ddd"},
                {".xdw", "application/vnd.fujixerox.docuworks"},
                {".xbd", "application/vnd.fujixerox.docuworks.binder"},
                {".oas", "application/vnd.fujitsu.oasys"},
                {".oa2", "application/vnd.fujitsu.oasys2"},
                {".oa3", "application/vnd.fujitsu.oasys3"},
                {".fg5", "application/vnd.fujitsu.oasysgp"},
                {".bh2", "application/vnd.fujitsu.oasysprs"},
                {".spl", "application/x-futuresplash"},
                {".fzs", "application/vnd.fuzzysheet"},
                {".g3", "image/g3fax"},
                {".gmx", "application/vnd.gmx"},
                {".gtw", "model/vnd.gtw"},
                {".txd", "application/vnd.genomatix.tuxedo"},
                {".ggb", "application/vnd.geogebra.file"},
                {".ggt", "application/vnd.geogebra.tool"},
                {".gdl", "model/vnd.gdl"},
                {".gex", "application/vnd.geometry-explorer"},
                {".gxt", "application/vnd.geonext"},
                {".g2w", "application/vnd.geoplan"},
                {".g3w", "application/vnd.geospace"},
                {".gsf", "application/x-font-ghostscript"},
                {".bdf", "application/x-font-bdf"},
                {".gtar", "application/x-gtar"},
                {".texinfo", "application/x-texinfo"},
                {".gnumeric", "application/x-gnumeric"},
                {".kml", "application/vnd.google-earth.kml+xml"},
                {".kmz", "application/vnd.google-earth.kmz"},
                {".gqf", "application/vnd.grafeq"},
                {".gif", "image/gif"},
                {".gv", "text/vnd.graphviz"},
                {".gac", "application/vnd.groove-account"},
                {".ghf", "application/vnd.groove-help"},
                {".gim", "application/vnd.groove-identity-message"},
                {".grv", "application/vnd.groove-injector"},
                {".gtm", "application/vnd.groove-tool-message"},
                {".tpl", "application/vnd.groove-tool-template"},
                {".vcg", "application/vnd.groove-vcard"},
                {".h261", "video/h261"},
                {".h263", "video/h263"},
                {".h264", "video/h264"},
                {".hpid", "application/vnd.hp-hpid"},
                {".hps", "application/vnd.hp-hps"},
                {".hdf", "application/x-hdf"},
                {".rip", "audio/vnd.rip"},
                {".hbci", "application/vnd.hbci"},
                {".jlt", "application/vnd.hp-jlyt"},
                {".pcl", "application/vnd.hp-pcl"},
                {".hpgl", "application/vnd.hp-hpgl"},
                {".hvs", "application/vnd.yamaha.hv-script"},
                {".hvd", "application/vnd.yamaha.hv-dic"},
                {".hvp", "application/vnd.yamaha.hv-voice"},
                {".sfd-hdstx", "application/vnd.hydrostatix.sof-data"},
                {".stk", "application/hyperstudio"},
                {".hal", "application/vnd.hal+xml"},
                {".html", "text/html"},
                {".irm", "application/vnd.ibm.rights-management"},
                {".sc", "application/vnd.ibm.secure-container"},
                {".ics", "text/calendar"},
                {".icc", "application/vnd.iccprofile"},
                {".ico", "image/x-icon"},
                {".igl", "application/vnd.igloader"},
                {".ief", "image/ief"},
                {".ivp", "application/vnd.immervision-ivp"},
                {".ivu", "application/vnd.immervision-ivu"},
                {".rif", "application/reginfo+xml"},
                {".3dml", "text/vnd.in3d.3dml"},
                {".spot", "text/vnd.in3d.spot"},
                {".igs", "model/iges"},
                {".i2g", "application/vnd.intergeo"},
                {".cdy", "application/vnd.cinderella"},
                {".xpw", "application/vnd.intercon.formnet"},
                {".fcs", "application/vnd.isac.fcs"},
                {".ipfix", "application/ipfix"},
                {".cer", "application/pkix-cert"},
                {".pki", "application/pkixcmp"},
                {".crl", "application/pkix-crl"},
                {".pkipath", "application/pkix-pkipath"},
                {".igm", "application/vnd.insors.igm"},
                {".rcprofile", "application/vnd.ipunplugged.rcprofile"},
                {".irp", "application/vnd.irepository.package+xml"},
                {".jad", "text/vnd.sun.j2me.app-descriptor"},
                {".jar", "application/java-archive"},
                {".class", "application/java-vm"},
                {".jnlp", "application/x-java-jnlp-file"},
                {".ser", "application/java-serialized-object"},
                {".java", "text/x-java-source,java"},
                {".js", "application/javascript"},
                {".json", "application/json"},
                {".joda", "application/vnd.joost.joda-archive"},
                {".jpm", "video/jpm"},
                {".jpeg, .jpg", "image/jpeg"},
                {".jpeg, .jpg", "image/x-citrix-jpeg"},
                {".pjpeg", "image/pjpeg"},
                {".jpgv", "video/jpeg"},
                {".ktz", "application/vnd.kahootz"},
                {".mmd", "application/vnd.chipnuts.karaoke-mmd"},
                {".karbon", "application/vnd.kde.karbon"},
                {".chrt", "application/vnd.kde.kchart"},
                {".kfo", "application/vnd.kde.kformula"},
                {".flw", "application/vnd.kde.kivio"},
                {".kon", "application/vnd.kde.kontour"},
                {".kpr", "application/vnd.kde.kpresenter"},
                {".ksp", "application/vnd.kde.kspread"},
                {".kwd", "application/vnd.kde.kword"},
                {".htke", "application/vnd.kenameaapp"},
                {".kia", "application/vnd.kidspiration"},
                {".kne", "application/vnd.kinar"},
                {".sse", "application/vnd.kodak-descriptor"},
                {".lasxml", "application/vnd.las.las+xml"},
                {".latex", "application/x-latex"},
                {".lbd", "application/vnd.llamagraphics.life-balance.desktop"},
                {".lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"},
                {".jam", "application/vnd.jam"},
                {".123", "application/vnd.lotus-1-2-3"},
                {".apr", "application/vnd.lotus-approach"},
                {".pre", "application/vnd.lotus-freelance"},
                {".nsf", "application/vnd.lotus-notes"},
                {".org", "application/vnd.lotus-organizer"},
                {".scm", "application/vnd.lotus-screencam"},
                {".lwp", "application/vnd.lotus-wordpro"},
                {".lvp", "audio/vnd.lucent.voice"},
                {".m3u", "audio/x-mpegurl"},
                {".m4v", "video/x-m4v"},
                {".hqx", "application/mac-binhex40"},
                {".portpkg", "application/vnd.macports.portpkg"},
                {".mgp", "application/vnd.osgeo.mapguide.package"},
                {".mrc", "application/marc"},
                {".mrcx", "application/marcxml+xml"},
                {".mxf", "application/mxf"},
                {".nbp", "application/vnd.wolfram.player"},
                {".ma", "application/mathematica"},
                {".mathml", "application/mathml+xml"},
                {".mbox", "application/mbox"},
                {".mc1", "application/vnd.medcalcdata"},
                {".mscml", "application/mediaservercontrol+xml"},
                {".cdkey", "application/vnd.mediastation.cdkey"},
                {".mwf", "application/vnd.mfer"},
                {".mfm", "application/vnd.mfmp"},
                {".msh", "model/mesh"},
                {".mads", "application/mads+xml"},
                {".mets", "application/mets+xml"},
                {".mods", "application/mods+xml"},
                {".meta4", "application/metalink4+xml"},
                {".mcd", "application/vnd.mcd"},
                {".flo", "application/vnd.micrografx.flo"},
                {".igx", "application/vnd.micrografx.igx"},
                {".es3", "application/vnd.eszigno3+xml"},
                {".mdb", "application/x-msaccess"},
                {".asf", "video/x-ms-asf"},
                {".exe", "application/x-msdownload"},
                {".cil", "application/vnd.ms-artgalry"},
                {".cab", "application/vnd.ms-cab-compressed"},
                {".ims", "application/vnd.ms-ims"},
                {".application", "application/x-ms-application"},
                {".clp", "application/x-msclip"},
                {".mdi", "image/vnd.ms-modi"},
                {".eot", "application/vnd.ms-fontobject"},
                {".xls", "application/vnd.ms-excel"},
                {".xlam", "application/vnd.ms-excel.addin.macroenabled.12"},
                {".xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"},
                {".xltm", "application/vnd.ms-excel.template.macroenabled.12"},
                {".xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"},
                {".chm", "application/vnd.ms-htmlhelp"},
                {".crd", "application/x-mscardfile"},
                {".lrm", "application/vnd.ms-lrm"},
                {".mvb", "application/x-msmediaview"},
                {".mny", "application/x-msmoney"},
                {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
                {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
                {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
                {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
                {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
                {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
                {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
                {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
                {".obd", "application/x-msbinder"},
                {".thmx", "application/vnd.ms-officetheme"},
                {".onetoc", "application/onenote"},
                {".pya", "audio/vnd.ms-playready.media.pya"},
                {".pyv", "video/vnd.ms-playready.media.pyv"},
                {".ppt", "application/vnd.ms-powerpoint"},
                {".ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"},
                {".sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"},
                {".pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"},
                {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"},
                {".potm", "application/vnd.ms-powerpoint.template.macroenabled.12"},
                {".mpp", "application/vnd.ms-project"},
                {".pub", "application/x-mspublisher"},
                {".scd", "application/x-msschedule"},
                {".xap", "application/x-silverlight-app"},
                {".stl", "application/vnd.ms-pki.stl"},
                {".cat", "application/vnd.ms-pki.seccat"},
                {".vsd", "application/vnd.visio"},
                {".vsdx", "application/vnd.visio2013"},
                {".wm", "video/x-ms-wm"},
                {".wma", "audio/x-ms-wma"},
                {".wax", "audio/x-ms-wax"},
                {".wmx", "video/x-ms-wmx"},
                {".wmd", "application/x-ms-wmd"},
                {".wpl", "application/vnd.ms-wpl"},
                {".wmz", "application/x-ms-wmz"},
                {".wmv", "video/x-ms-wmv"},
                {".wvx", "video/x-ms-wvx"},
                {".wmf", "application/x-msmetafile"},
                {".trm", "application/x-msterminal"},
                {".doc", "application/msword"},
                {".docm", "application/vnd.ms-word.document.macroenabled.12"},
                {".dotm", "application/vnd.ms-word.template.macroenabled.12"},
                {".wri", "application/x-mswrite"},
                {".wps", "application/vnd.ms-works"},
                {".xbap", "application/x-ms-xbap"},
                {".xps", "application/vnd.ms-xpsdocument"},
                {".mid", "audio/midi"},
                {".mpy", "application/vnd.ibm.minipay"},
                {".afp", "application/vnd.ibm.modcap"},
                {".rms", "application/vnd.jcp.javame.midlet-rms"},
                {".tmo", "application/vnd.tmobile-livetv"},
                {".prc", "application/x-mobipocket-ebook"},
                {".mbk", "application/vnd.mobius.mbk"},
                {".dis", "application/vnd.mobius.dis"},
                {".plc", "application/vnd.mobius.plc"},
                {".mqy", "application/vnd.mobius.mqy"},
                {".msl", "application/vnd.mobius.msl"},
                {".txf", "application/vnd.mobius.txf"},
                {".daf", "application/vnd.mobius.daf"},
                {".fly", "text/vnd.fly"},
                {".mpc", "application/vnd.mophun.certificate"},
                {".mpn", "application/vnd.mophun.application"},
                {".mj2", "video/mj2"},
                {".mpga", "audio/mpeg"},
                {".mxu", "video/vnd.mpegurl"},
                {".mpeg", "video/mpeg"},
                {".m21", "application/mp21"},
                {".mp4a", "audio/mp4"},
                {".mp4", "video/mp4"},
                {".mp4", "application/mp4"},
                {".m3u8", "application/vnd.apple.mpegurl"},
                {".mus", "application/vnd.musician"},
                {".msty", "application/vnd.muvee.style"},
                {".mxml", "application/xv+xml"},
                {".ngdat", "application/vnd.nokia.n-gage.data"},
                {".n-gage", "application/vnd.nokia.n-gage.symbian.install"},
                {".ncx", "application/x-dtbncx+xml"},
                {".nc", "application/x-netcdf"},
                {".nlu", "application/vnd.neurolanguage.nlu"},
                {".dna", "application/vnd.dna"},
                {".nnd", "application/vnd.noblenet-directory"},
                {".nns", "application/vnd.noblenet-sealer"},
                {".nnw", "application/vnd.noblenet-web"},
                {".rpst", "application/vnd.nokia.radio-preset"},
                {".rpss", "application/vnd.nokia.radio-presets"},
                {".n3", "text/n3"},
                {".edm", "application/vnd.novadigm.edm"},
                {".edx", "application/vnd.novadigm.edx"},
                {".ext", "application/vnd.novadigm.ext"},
                {".gph", "application/vnd.flographit"},
                {".ecelp4800", "audio/vnd.nuera.ecelp4800"},
                {".ecelp7470", "audio/vnd.nuera.ecelp7470"},
                {".ecelp9600", "audio/vnd.nuera.ecelp9600"},
                {".oda", "application/oda"},
                {".ogx", "application/ogg"},
                {".oga", "audio/ogg"},
                {".ogv", "video/ogg"},
                {".dd2", "application/vnd.oma.dd2+xml"},
                {".oth", "application/vnd.oasis.opendocument.text-web"},
                {".opf", "application/oebps-package+xml"},
                {".qbo", "application/vnd.intu.qbo"},
                {".oxt", "application/vnd.openofficeorg.extension"},
                {".osf", "application/vnd.yamaha.openscoreformat"},
                {".weba", "audio/webm"},
                {".webm", "video/webm"},
                {".odc", "application/vnd.oasis.opendocument.chart"},
                {".otc", "application/vnd.oasis.opendocument.chart-template"},
                {".odb", "application/vnd.oasis.opendocument.database"},
                {".odf", "application/vnd.oasis.opendocument.formula"},
                {".odft", "application/vnd.oasis.opendocument.formula-template"},
                {".odg", "application/vnd.oasis.opendocument.graphics"},
                {".otg", "application/vnd.oasis.opendocument.graphics-template"},
                {".odi", "application/vnd.oasis.opendocument.image"},
                {".oti", "application/vnd.oasis.opendocument.image-template"},
                {".odp", "application/vnd.oasis.opendocument.presentation"},
                {".otp", "application/vnd.oasis.opendocument.presentation-template"},
                {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
                {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
                {".odt", "application/vnd.oasis.opendocument.text"},
                {".odm", "application/vnd.oasis.opendocument.text-master"},
                {".ott", "application/vnd.oasis.opendocument.text-template"},
                {".ktx", "image/ktx"},
                {".sxc", "application/vnd.sun.xml.calc"},
                {".stc", "application/vnd.sun.xml.calc.template"},
                {".sxd", "application/vnd.sun.xml.draw"},
                {".std", "application/vnd.sun.xml.draw.template"},
                {".sxi", "application/vnd.sun.xml.impress"},
                {".sti", "application/vnd.sun.xml.impress.template"},
                {".sxm", "application/vnd.sun.xml.math"},
                {".sxw", "application/vnd.sun.xml.writer"},
                {".sxg", "application/vnd.sun.xml.writer.global"},
                {".stw", "application/vnd.sun.xml.writer.template"},
                {".otf", "application/x-font-otf"},
                {".osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"},
                {".dp", "application/vnd.osgi.dp"},
                {".pdb", "application/vnd.palm"},
                {".p", "text/x-pascal"},
                {".paw", "application/vnd.pawaafile"},
                {".pclxl", "application/vnd.hp-pclxl"},
                {".efif", "application/vnd.picsel"},
                {".pcx", "image/x-pcx"},
                {".psd", "image/vnd.adobe.photoshop"},
                {".prf", "application/pics-rules"},
                {".pic", "image/x-pict"},
                {".chat", "application/x-chat"},
                {".p10", "application/pkcs10"},
                {".p12", "application/x-pkcs12"},
                {".p7m", "application/pkcs7-mime"},
                {".p7s", "application/pkcs7-signature"},
                {".p7r", "application/x-pkcs7-certreqresp"},
                {".p7b", "application/x-pkcs7-certificates"},
                {".p8", "application/pkcs8"},
                {".plf", "application/vnd.pocketlearn"},
                {".pnm", "image/x-portable-anymap"},
                {".pbm", "image/x-portable-bitmap"},
                {".pcf", "application/x-font-pcf"},
                {".pfr", "application/font-tdpfr"},
                {".pgn", "application/x-chess-pgn"},
                {".pgm", "image/x-portable-graymap"},
                {".png", "image/png"},
                {".png", "image/x-citrix-png"},
                {".png", "image/x-png"},
                {".ppm", "image/x-portable-pixmap"},
                {".pskcxml", "application/pskc+xml"},
                {".pml", "application/vnd.ctc-posml"},
                {".ai", "application/postscript"},
                {".pfa", "application/x-font-type1"},
                {".pbd", "application/vnd.powerbuilder6"},
                {".pgp", "application/pgp-encrypted"},
                {".pgp", "application/pgp-signature"},
                {".box", "application/vnd.previewsystems.box"},
                {".ptid", "application/vnd.pvi.ptid1"},
                {".pls", "application/pls+xml"},
                {".str", "application/vnd.pg.format"},
                {".ei6", "application/vnd.pg.osasli"},
                {".dsc", "text/prs.lines.tag"},
                {".psf", "application/x-font-linux-psf"},
                {".qps", "application/vnd.publishare-delta-tree"},
                {".wg", "application/vnd.pmi.widget"},
                {".qxd", "application/vnd.quark.quarkxpress"},
                {".esf", "application/vnd.epson.esf"},
                {".msf", "application/vnd.epson.msf"},
                {".ssf", "application/vnd.epson.ssf"},
                {".qam", "application/vnd.epson.quickanime"},
                {".qfx", "application/vnd.intu.qfx"},
                {".qt", "video/quicktime"},
                {".rar", "application/x-rar-compressed"},
                {".ram", "audio/x-pn-realaudio"},
                {".rmp", "audio/x-pn-realaudio-plugin"},
                {".rsd", "application/rsd+xml"},
                {".rm", "application/vnd.rn-realmedia"},
                {".bed", "application/vnd.realvnc.bed"},
                {".mxl", "application/vnd.recordare.musicxml"},
                {".musicxml", "application/vnd.recordare.musicxml+xml"},
                {".rnc", "application/relax-ng-compact-syntax"},
                {".rdz", "application/vnd.data-vision.rdz"},
                {".rdf", "application/rdf+xml"},
                {".rp9", "application/vnd.cloanto.rp9"},
                {".jisp", "application/vnd.jisp"},
                {".rtf", "application/rtf"},
                {".rtx", "text/richtext"},
                {".link66", "application/vnd.route66.link66+xml"},
                {".rss, .xml", "application/rss+xml"},
                {".shf", "application/shf+xml"},
                {".st", "application/vnd.sailingtracker.track"},
                {".svg", "image/svg+xml"},
                {".sus", "application/vnd.sus-calendar"},
                {".sru", "application/sru+xml"},
                {".setpay", "application/set-payment-initiation"},
                {".setreg", "application/set-registration-initiation"},
                {".sema", "application/vnd.sema"},
                {".semd", "application/vnd.semd"},
                {".semf", "application/vnd.semf"},
                {".see", "application/vnd.seemail"},
                {".snf", "application/x-font-snf"},
                {".spq", "application/scvp-vp-request"},
                {".spp", "application/scvp-vp-response"},
                {".scq", "application/scvp-cv-request"},
                {".scs", "application/scvp-cv-response"},
                {".sdp", "application/sdp"},
                {".etx", "text/x-setext"},
                {".movie", "video/x-sgi-movie"},
                {".ifm", "application/vnd.shana.informed.formdata"},
                {".itp", "application/vnd.shana.informed.formtemplate"},
                {".iif", "application/vnd.shana.informed.interchange"},
                {".ipk", "application/vnd.shana.informed.package"},
                {".tfi", "application/thraud+xml"},
                {".shar", "application/x-shar"},
                {".rgb", "image/x-rgb"},
                {".slt", "application/vnd.epson.salt"},
                {".aso", "application/vnd.accpac.simply.aso"},
                {".imp", "application/vnd.accpac.simply.imp"},
                {".twd", "application/vnd.simtech-mindmapper"},
                {".csp", "application/vnd.commonspace"},
                {".saf", "application/vnd.yamaha.smaf-audio"},
                {".mmf", "application/vnd.smaf"},
                {".spf", "application/vnd.yamaha.smaf-phrase"},
                {".teacher", "application/vnd.smart.teacher"},
                {".svd", "application/vnd.svd"},
                {".rq", "application/sparql-query"},
                {".srx", "application/sparql-results+xml"},
                {".gram", "application/srgs"},
                {".grxml", "application/srgs+xml"},
                {".ssml", "application/ssml+xml"},
                {".skp", "application/vnd.koan"},
                {".sgml", "text/sgml"},
                {".sdc", "application/vnd.stardivision.calc"},
                {".sda", "application/vnd.stardivision.draw"},
                {".sdd", "application/vnd.stardivision.impress"},
                {".smf", "application/vnd.stardivision.math"},
                {".sdw", "application/vnd.stardivision.writer"},
                {".sgl", "application/vnd.stardivision.writer-global"},
                {".sm", "application/vnd.stepmania.stepchart"},
                {".sit", "application/x-stuffit"},
                {".sitx", "application/x-stuffitx"},
                {".sdkm", "application/vnd.solent.sdkm+xml"},
                {".xo", "application/vnd.olpc-sugar"},
                {".au", "audio/basic"},
                {".wqd", "application/vnd.wqd"},
                {".sis", "application/vnd.symbian.install"},
                {".smi", "application/smil+xml"},
                {".xsm", "application/vnd.syncml+xml"},
                {".bdm", "application/vnd.syncml.dm+wbxml"},
                {".xdm", "application/vnd.syncml.dm+xml"},
                {".sv4cpio", "application/x-sv4cpio"},
                {".sv4crc", "application/x-sv4crc"},
                {".sbml", "application/sbml+xml"},
                {".tsv", "text/tab-separated-values"},
                {".tiff", "image/tiff"},
                {".tao", "application/vnd.tao.intent-module-archive"},
                {".tar", "application/x-tar"},
                {".tcl", "application/x-tcl"},
                {".tex", "application/x-tex"},
                {".tfm", "application/x-tex-tfm"},
                {".tei", "application/tei+xml"},
                {".txt", "text/plain"},
                {".dxp", "application/vnd.spotfire.dxp"},
                {".sfs", "application/vnd.spotfire.sfs"},
                {".tsd", "application/timestamped-data"},
                {".tpt", "application/vnd.trid.tpt"},
                {".mxs", "application/vnd.triscape.mxs"},
                {".t", "text/troff"},
                {".tra", "application/vnd.trueapp"},
                {".ttf", "application/x-font-ttf"},
                {".ttl", "text/turtle"},
                {".umj", "application/vnd.umajin"},
                {".uoml", "application/vnd.uoml+xml"},
                {".unityweb", "application/vnd.unity"},
                {".ufd", "application/vnd.ufdl"},
                {".uri", "text/uri-list"},
                {".utz", "application/vnd.uiq.theme"},
                {".ustar", "application/x-ustar"},
                {".uu", "text/x-uuencode"},
                {".vcs", "text/x-vcalendar"},
                {".vcf", "text/x-vcard"},
                {".vcd", "application/x-cdlink"},
                {".vsf", "application/vnd.vsf"},
                {".wrl", "model/vrml"},
                {".vcx", "application/vnd.vcx"},
                {".mts", "model/vnd.mts"},
                {".vtu", "model/vnd.vtu"},
                {".vis", "application/vnd.visionary"},
                {".viv", "video/vnd.vivo"},
                {".ccxml", "application/ccxml+xml,"},
                {".vxml", "application/voicexml+xml"},
                {".src", "application/x-wais-source"},
                {".wbxml", "application/vnd.wap.wbxml"},
                {".wbmp", "image/vnd.wap.wbmp"},
                {".wav", "audio/x-wav"},
                {".davmount", "application/davmount+xml"},
                {".woff", "application/x-font-woff"},
                {".wspolicy", "application/wspolicy+xml"},
                {".webp", "image/webp"},
                {".wtb", "application/vnd.webturbo"},
                {".wgt", "application/widget"},
                {".hlp", "application/winhlp"},
                {".wml", "text/vnd.wap.wml"},
                {".wmls", "text/vnd.wap.wmlscript"},
                {".wmlsc", "application/vnd.wap.wmlscriptc"},
                {".wpd", "application/vnd.wordperfect"},
                {".stf", "application/vnd.wt.stf"},
                {".wsdl", "application/wsdl+xml"},
                {".xbm", "image/x-xbitmap"},
                {".xpm", "image/x-xpixmap"},
                {".xwd", "image/x-xwindowdump"},
                {".der", "application/x-x509-ca-cert"},
                {".fig", "application/x-xfig"},
                {".xhtml", "application/xhtml+xml"},
                {".xml", "application/xml"},
                {".xdf", "application/xcap-diff+xml"},
                {".xenc", "application/xenc+xml"},
                {".xer", "application/patch-ops-error+xml"},
                {".rl", "application/resource-lists+xml"},
                {".rs", "application/rls-services+xml"},
                {".rld", "application/resource-lists-diff+xml"},
                {".xslt", "application/xslt+xml"},
                {".xop", "application/xop+xml"},
                {".xpi", "application/x-xpinstall"},
                {".xspf", "application/xspf+xml"},
                {".xul", "application/vnd.mozilla.xul+xml"},
                {".xyz", "chemical/x-xyz"},
                {".yaml", "text/yaml"},
                {".yang", "application/yang"},
                {".yin", "application/yin+xml"},
                {".zir", "application/vnd.zul"},
                {".zip", "application/zip"},
                {".zmm", "application/vnd.handheld-entertainment+xml"},
                {".zaz", "application/vnd.zzazz.deck+xml"}
        };

        //Try to isolate extention from filename variable
        std::string extention;
        auto dotPos = filename.find_last_of('.');
        if(dotPos != std::string::npos)
            extention = filename.substr(dotPos, filename.size() - dotPos);
        else
            extention = "." + filename;

        //Find it
        auto iter = known_mimetypes.find(extention);
        if(iter == known_mimetypes.end())
            return known_mimetypes[".bin"];
        return iter->second;
    }

    Socket::Status Http::send(Socket *socket) const
    {
        std::string data = construct(socket->get_remote_address());
        size_t sent = 0;
        fr::Socket::Status state;
        do
        {
            state = socket->send_raw(&data[0], data.size(), sent);
        } while(state == fr::Socket::Status::WouldBlock);
        return state;
    }

    Socket::Status Http::receive(Socket *socket)
    {
        char recv_buffer[RECV_CHUNK_SIZE];
        fr::Socket::Status state;
        size_t total_received = 0;
        size_t received = 0;
        do
        {
            //Receive the request
            auto status = socket->receive_raw(recv_buffer, RECV_CHUNK_SIZE, received);
            total_received += received;
            if(status != Socket::Status::Success)
            {
                if(total_received == 0)
                    return status;
                if(status == Socket::Status::WouldBlock)
                    continue;
                return Socket::Status::Disconnected;
            }

            //Parse it
            state = parse(recv_buffer, received);
        } while(state == fr::Socket::Status::NotEnoughData);

        return state;
    }

    void Http::set_version(Http::RequestVersion v)
    {
        version = v;
    }

    Http::RequestVersion Http::get_version() const
    {
        return version;
    }
}